using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Text.RegularExpressions;

namespace SubSonic
{
    public class InlineQuery
    {
        private QueryCommand command;

        public InlineQuery()
        {
            command = new QueryCommand("", this.command.ProviderName);
        }

        public InlineQuery(string providerName)
        {
            command = new QueryCommand("", providerName);
        }

        public void Execute(string sql)
        {
            Execute(sql, new object[0]);
        }

        public void Execute(string sql, params object[] values)
        {
            DataService.ExecuteQuery(GetCommand(sql, values));
        }

        public TResult ExecuteScalar<TResult>(string sql)
        {
            return ExecuteScalar<TResult>(sql, new object[0]);
        }

        public TResult ExecuteScalar<TResult>(string sql, params object[] values)
        {
            object oResult = DataService.ExecuteScalar(GetCommand(sql, values));
            TResult result = (TResult)Utilities.Utility.ChangeType(oResult, typeof(TResult));
            return result;
        }
        public List<T> ExecuteTypedList<T>(string sql) where T : new() {
            return ExecuteTypedList<T>(sql, new object[0]);
        }
        public List<T> ExecuteTypedList<T>(string sql, params object[] values) where T : new() {
            QueryCommand cmd= GetCommand(sql, values);

            List<T> result = new List<T>();
            System.Reflection.PropertyInfo[] props = null;
            IDataReader rdr = null;
            Type iType = typeof(T);

            try {
                rdr = DataService.GetReader(cmd);
            } catch (Exception x) {
                SqlQueryException ex = new SqlQueryException(x.Message);
                throw ex;
            }

            if (iType is IActiveRecord) {

                //load it
                while (rdr.Read()) {
                    T item = new T();
                    //set to ActiveRecord
                    IActiveRecord arItem = (IActiveRecord)item;

                    arItem.Load(rdr);
                    result.Add(item);
                }
            } else {
                //coerce the values, using some light reflection
                props = iType.GetProperties();

                //set the values
                System.Reflection.PropertyInfo prop = null;
                while (rdr.Read()) {
                    T item = new T();
                    Type propType = null;

                    for (int i = 0; i < rdr.FieldCount; i++) {
                        prop = iType.GetProperty(rdr.GetName(i));
                        if (prop != null) {
                            prop.SetValue(item, rdr.GetValue(i), null);
                        }
                    }
                    result.Add(item);
                }
            }

            rdr.Close();

            return result;
        }
        public virtual ListType ExecuteAsCollection<ListType>(string sql)
        where ListType : IAbstractList, new() {
            return ExecuteAsCollection<ListType>(sql, new object[0]);
        }
        public virtual ListType ExecuteAsCollection<ListType>(string sql, params object[] values)
    where ListType : IAbstractList, new() {
            QueryCommand cmd = GetCommand(sql, values);

            ListType list = new ListType();
            try {
                IDataReader rdr = DataService.GetReader(cmd);
                list.LoadAndCloseReader(rdr);
            } catch (Exception x) {
                SqlQueryException ex =new SqlQueryException(x.Message);
                throw ex;
            }

            return list;
        }
        public IDataReader ExecuteReader(string sql)
        {
            return ExecuteReader(sql, new object[0]);
        }

        public IDataReader ExecuteReader(string sql, params object[] values)
        {
            IDataReader result = DataService.GetReader(GetCommand(sql, values));
            return result;
        }

        public QueryCommand GetCommand(string sql)
        {
            return GetCommand(sql, new object[0]);
        }

        public QueryCommand GetCommand(string sql, params object[] values)
        {
            if(command == null)
                command = new QueryCommand(sql, this.command.ProviderName);

            command.CommandSql = sql;
            if(values.Length > 0)
                LoadCommandParams(sql, values);
            return command;
        }

        private void LoadCommandParams(string sql, object[] values)
        {
            //load up the params
            List<string> paramList = ParseParameters(sql);

            //validate it
            if(paramList.Count != values.Length)
            {
                throw new SqlQueryException(
                    "The parameter count doesn't match up with the values entered - this could be our fault with our parser; please check the list to make sure the count adds up, and if it does, please add some spacing around the parameters in the list");
            }

            int indexer = 0;
            foreach(string s in paramList)
            {
                command.Parameters.Add(s, values[indexer]);
                indexer++;
            }
        }

        private static List<string> ParseParameters(string sql)
        {

            List<string> result = new List<string>();
            Regex paramReg = new Regex(@"@\w*");

            MatchCollection matches = paramReg.Matches(sql + " ");
            foreach(Match m in matches)
                result.Add(m.Value);

            return result;
        }
    }
}